package fimuni.pb138.repositar;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.basex.core.cmd.Open;
import org.basex.server.ClientQuery;
import org.basex.server.ClientSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

@Service
public class WSDLService {

	private ClientSession session;
	private static final Logger logger = LoggerFactory
			.getLogger(WSDLService.class);

	/**
	 * Constructor for WSDLService Establishes connection to BaseX database
	 */
	public WSDLService() {
		try {
			session = new ClientSession(ArtifactUtils.DATABASE_SERVER,
					ArtifactUtils.DATABASE_PORT,
					ArtifactUtils.DATABASE_USERNAME,
					ArtifactUtils.DATABASE_PASSWORD);
			session.execute(new Open(ArtifactUtils.DATABASE_NAME));
		} catch (IOException e) {
			logger.error(e.getLocalizedMessage());
		}
	}

	/**
	 * Inserts WSDL file into database. Creates new version of file If it
	 * already exist in DB, or creates new record for the file
	 * 
	 * @param file
	 *            file to be inserted into DB
	 * @return
	 */
	public String insertWSDL(WSDLFile file) {
		try {
			ClientQuery cq = session.query("count(/wsdls/wsdl[name=\""
					+ file.getName() + "\"])");
			boolean exists = !cq.execute().equals("0");
			if (exists) {
				StringBuilder query = new StringBuilder();
				query.append("insert node");
				cq = session.query("max(/wsdls/wsdl[name=\"" + file.getName()
						+ "\"]/versions/version/@number)");
				int lastVersionNumber = Integer.valueOf(cq.execute()) + 1;
				query.append("<version number=\"" + lastVersionNumber
						+ "\" fileName=\"" + (file.getFileName()) + "\">");
				query.append("<operations>");
				for (WSDLOperation operation : file.getOperations()) {
					query.append("<operation name=\"" + operation.getName()
							+ "\">");
					query.append("<request>" + operation.getRequest()
							+ "</request>");
					query.append("<response>" + operation.getResponse()
							+ "</response>");
					query.append("</operation>");
				}
				query.append("</operations>");
				query.append("</version>");
				query.append("into /wsdls/wsdl[name=\"" + file.getName()
						+ "\"]/versions");
				cq = session.query(query.toString());
			} else {
				StringBuilder query = new StringBuilder();
				query.append("insert node");
				cq = session.query("max(/wsdls/wsdl/@id)");
				int lastId = Integer.valueOf(cq.execute()) + 1;
				query.append("<wsdl id=\"" + lastId + "\">");
				query.append("<name>" + file.getName() + "</name>");
				query.append("<versions>");
				query.append("<version number=\"1\"  fileName=\""
						+ (file.getFileName()) + "\">");
				query.append("<operations>");
				for (WSDLOperation operation : file.getOperations()) {
					query.append("<operation name=\"" + operation.getName()
							+ "\">");
					query.append("<request>" + operation.getRequest()
							+ "</request>");
					query.append("<response>" + operation.getResponse()
							+ "</response>");
					query.append("</operation>");
				}
				query.append("</operations>");
				query.append("</version>");
				query.append("</versions>");
				query.append("</wsdl>");
				query.append("into /wsdls");
				cq = session.query(query.toString());
			}
			cq.execute();
		} catch (IOException e) {
			logger.error(e.getLocalizedMessage());
		}
		return "";
	}

	/**
	 * Finds artifact by id in database
	 * 
	 * @param id id of wanted artifact
	 * @return list of all versions of specified artifact
	 */
	public List<WSDLFile> findWSDLbyID(long id) {
		List<WSDLFile> wsdlFiles = new ArrayList<>();
		try {
			ClientQuery cq = session.query("count(/wsdls/wsdl[@id=\"" + id
					+ "\"]/versions/version)");
			int versionCount = Integer.parseInt(cq.execute());
			cq = session.query("/wsdls/wsdl[@id=\"" + id + "\"]/name/text()");
			String name = cq.execute();
			for (int i = 1; i <= versionCount; i++) {
				WSDLFile wsdlFile = new WSDLFile();
				wsdlFile.setId(id);
				wsdlFile.setName(name);
				wsdlFile.setVersion(i);

				cq = session.query("string(/wsdls/wsdl[@id=\"" + id
						+ "\"]/versions/version[" + i + "]/@fileName)");
				wsdlFile.setFileName(cq.execute());

				cq = session.query("count(/wsdls/wsdl[@id=\"" + id
						+ "\"]/versions/version[" + i
						+ "]/operations/operation)");
				int operationCount = Integer.parseInt(cq.execute());
				List<WSDLOperation> operations = new ArrayList<>();
				for (int o = 1; i <= operationCount; i++) {
					WSDLOperation op = new WSDLOperation();
					cq = session.query("/wsdls/wsdl[@id=\"" + id
							+ "\"]/versions/version[" + i
							+ "]/operations/operation[" + o + "]/@name");
					op.setName(cq.execute());
					cq = session.query("/wsdls/wsdl[@id=\"" + id
							+ "\"]/versions/version[" + i
							+ "]/operations/operation[" + o
							+ "]/request/text()");
					op.setRequest(cq.execute());
					cq = session.query("/wsdls/wsdl[@id=\"" + id
							+ "\"]/versions/version[" + i
							+ "]/operations/operation[" + o
							+ "]/response/text()");
					op.setResponse(cq.execute());
					operations.add(op);
				}
				wsdlFile.setOperations(operations);

				wsdlFiles.add(wsdlFile);
			}

		} catch (IOException e) {
			logger.error(e.getLocalizedMessage());
		}
		return wsdlFiles;
	}
	
	/**
	 * Finds artifact by id in database
	 * 
	 * @param id id of wanted artifact
	 * @param version version of wanted artifact
	 * @return version of specified artifact
	 */
	public WSDLFile findWSDLbyIDandVersion(long id, long version) {
		List<WSDLFile> wsdlFiles = findWSDLbyID(id);
		for(WSDLFile file : wsdlFiles) {
			if(file.getVersion() == version) {
				return file;
			}
		}
		
		return null;
	}

	/**
	 * Finds artifact by its metadata in database
	 * 
	 * @param name
	 *            name of the artifact
	 * @param operation
	 *            wsdl operaion
	 * @param response
	 *            operation response
	 * @param request
	 *            operation request
	 * @return list of all versions of specified artifact
	 */
	public List<WSDLFile> findWSDLbyMetadata(String name, String operation,
			String response, String request) {
		
		if(name == null)
			name = "";
		if(operation == null)
			operation = "";
		if(response == null)
			response = "";
		if(request == null)
			request = "";

		List<WSDLFile> wsdlFiles = getAllWSDLs();
	
		// filter all not wanted versions of specified artifact
		Iterator<WSDLFile> i = wsdlFiles.iterator();
		while (i.hasNext()) {
			WSDLFile file = i.next();
			if (!file.getName().equals(name) && !name.isEmpty()) {
				i.remove();
				continue;
			}
			List<WSDLOperation> operations = file.getOperations();
			for(WSDLOperation op : operations){
				if( (!op.getName().equals(operation) && !operation.isEmpty())
						|| (!op.getRequest().equals(request) && !request.isEmpty())
						|| (!op.getResponse().equals(response) && !response.isEmpty()) ) {
					i.remove();
					break;
				}
			}
		}

		return wsdlFiles;
	}

	/**
	 * Stores uploaded file to database
	 * 
	 * @param file uploaded file
	 */
	public void processWSDL(MultipartFile file) {
		String fileName = ArtifactUtils.saveFile(file);
		WSDLFile wsdlFile = new WSDLFile();
		String oldName = file.getOriginalFilename();
		wsdlFile.setName(oldName);
		wsdlFile.setFileName(fileName);

		String operationStrings = XQueryProcessor
				.processQuery(ArtifactUtils.XQUERY_WSDL_OPERATIONS.replace(
						"DOCNAME",
						ArtifactUtils.STORAGE_PATH.replace("\\", "/") + wsdlFile.getFileName()));
		List<WSDLOperation> operations = new ArrayList<>();
		if (!operationStrings.isEmpty()) {
			String[] oneOperation = operationStrings.split("\\|");
			for (String s : oneOperation) {
				String[] split = s.split("\\#");
				if(split.length != 3){
					logger.error("Invalid operation - missing name, request or response (" + split.toString() + ")");
					continue;
				}
				WSDLOperation op = new WSDLOperation();
				op.setName(split[0]);
				op.setRequest(split[1]);
				op.setResponse(split[2]);
				operations.add(op);
			}
		}
		wsdlFile.setOperations(operations);

		insertWSDL(wsdlFile);
	}
	
	/**
	 * Get last versions of all WSDL files from database
	 * 
	 * @return list of all WSDL files
	 */
	public List<WSDLFile> getAllWSDLs() {
		List<WSDLFile> WSDLList = new ArrayList<>();
		try {
			ClientQuery cq = session.query("count(/wsdls/wsdl)");
			int wsdlCount = Integer.parseInt(cq.execute());
			for(int i = 1; i <= wsdlCount; i++) {
				List<WSDLFile> versionList = findWSDLbyID(i);
				WSDLFile wsdl = versionList.get(versionList.size()-1);
				WSDLList.add(wsdl);
			}
		}
		catch(IOException e) {
			logger.error(e.getLocalizedMessage());
		}
		
		return WSDLList;
	}
}

















